<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.14.0"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>DM-CtrlH7-BF-DevProgram: bsp_can</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<script type="text/javascript" src="clipboard.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript" src="cookie.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr id="projectrow">
  <td id="projectlogo"><img alt="Logo" src="50x5.png"/></td>
  <td id="projectalign">
   <div id="projectname">DM-CtrlH7-BF-DevProgram<span id="projectnumber">&#160;beta 0.1</span>
   </div>
   <div id="projectbrief">C.ONE Studio Damiao Development Board Framework</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.14.0 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search/",'.html');
</script>
<script type="text/javascript">
$(function() { codefold.init(); });
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
  initMenu('',true,false,'search.php','Search',true);
  $(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
  <div id="nav-tree">
    <div id="nav-tree-contents">
      <div id="nav-sync" class="sync"></div>
    </div>
  </div>
  <div id="splitbar" style="-moz-user-select:none;" 
       class="ui-resizable-handle">
  </div>
</div>
<script type="text/javascript">
$(function(){initNavTree('md__c_1_2_users_2_a_s_u_s_2_desktop_2dm-ctrl_h7-balance-9025test_2bsp_2can_2bsp__can.html','',''); });
</script>
<div id="container">
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<div id="MSearchResults">
<div class="SRPage">
<div id="SRIndex">
<div id="SRResults"></div>
<div class="SRStatus" id="Loading">Loading...</div>
<div class="SRStatus" id="Searching">Searching...</div>
<div class="SRStatus" id="NoMatches">No Matches</div>
</div>
</div>
</div>
</div>

<div><div class="header">
  <div class="headertitle"><div class="title">bsp_can </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p><a class="anchor" id="autotoc_md0"></a></p>
<p align="right"></p>
<p><a href="#" onclick="location.href='mai'+'lto:'+'neo'+'zn'+'g1@'+'hn'+'u.e'+'du'+'.cn'; return false;">neozn<span class="obfuscator">.nosp@m.</span>g1@h<span class="obfuscator">.nosp@m.</span>nu.ed<span class="obfuscator">.nosp@m.</span>u.cn</a></p>
<h1 class="doxsection"><a class="anchor" id="autotoc_md1"></a>
请注意使用CAN设备的时候务必保证总线只接入了2个终端电阻！开发板一般都有一个，6020电机、c620/c610电调、LK电机也都有终端电阻，注意把多于2个的全部断开（通过拨码）</h1>
<h2 class="doxsection"><a class="anchor" id="autotoc_md2"></a>
使用说明</h2>
<p>若你希望新增一个基于CAN的module，首先在该模块下应该有一个包含<span class="tt">can_instance</span>指针的module结构体（或当功能简单的时候，可以是单独存在的<span class="tt">can_instance</span>，但不推荐这样做）。</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md3"></a>
代码结构</h2>
<p>.h文件内包括了外部接口和类型定义,以及模块对应的宏。c文件内为私有函数和外部接口的定义。</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md4"></a>
类型定义</h2>
<div class="fragment"><div class="line"><span class="preprocessor">#define MX_REGISTER_DEVICE_CNT 12  </span><span class="comment">// maximum number of device can be registered to CAN service, this number depends on the load of CAN bus.</span></div>
<div class="line"><span class="preprocessor">#define MX_CAN_FILTER_CNT (4 * 14) </span><span class="comment">// temporarily useless</span></div>
<div class="line"><span class="preprocessor">#define DEVICE_CAN_CNT 2           </span><span class="comment">// CAN1,CAN2</span></div>
<div class="line"> </div>
<div class="line"><span class="comment">/* can instance typedef, every module registered to CAN should have this variable */</span></div>
<div class="line"><span class="keyword">typedef</span> <span class="keyword">struct </span><a class="code hl_struct" href="struct__.html">_</a></div>
<div class="line">{</div>
<div class="line">    CAN_HandleTypeDef *can_handle; <span class="comment">// can句柄</span></div>
<div class="line">    CAN_TxHeaderTypeDef txconf;    <span class="comment">// CAN报文发送配置</span></div>
<div class="line">    uint32_t tx_id;                <span class="comment">// 发送id</span></div>
<div class="line">    uint32_t tx_mailbox;           <span class="comment">// CAN消息填入的邮箱号</span></div>
<div class="line">    uint8_t tx_buff[8];            <span class="comment">// 发送缓存,发送消息长度可以通过CANSetDLC()设定,最大为8</span></div>
<div class="line">    uint8_t rx_buff[8];            <span class="comment">// 接收缓存,最大消息长度为8</span></div>
<div class="line">    uint32_t rx_id;                <span class="comment">// 接收id</span></div>
<div class="line">    uint8_t rx_len;                <span class="comment">// 接收长度,可能为0-8</span></div>
<div class="line">    <span class="comment">// 接收的回调函数,用于解析接收到的数据</span></div>
<div class="line">    void (*can_module_callback)(<span class="keyword">struct </span><a class="code hl_struct" href="struct__.html">_</a> *); <span class="comment">// callback needs an instance to tell among registered ones</span></div>
<div class="line">    <span class="keywordtype">void</span> *id;                                <span class="comment">// 使用can外设的模块指针(即id指向的模块拥有此can实例,是父子关系)</span></div>
<div class="line">} CANInstance;</div>
<div class="line"> </div>
<div class="line"><span class="keyword">typedef</span> <span class="keyword">struct </span></div>
<div class="line">{</div>
<div class="line">    CAN_HandleTypeDef* can_handle;</div>
<div class="line">    uint32_t tx_id;</div>
<div class="line">    uint32_t rx_id;</div>
<div class="line">    void (*can_module_callback)(can_instance*);</div>
<div class="line">    <span class="keywordtype">void</span>* id;</div>
<div class="line">} can_instance_config;</div>
<div class="line"> </div>
<div class="line"><span class="keyword">typedef</span> void (*can_callback)(can_instance*);</div>
<div class="ttc" id="astruct___html"><div class="ttname"><a href="struct__.html">_</a></div><div class="ttdef"><b>Definition</b> bsp_can.h:16</div></div>
</div><!-- fragment --><ul>
<li><span class="tt">MX_REGISTER_DEVICE_CNT</span>是最大的CAN设备注册数量，当每个设备的发送频率都较高时，设备过多会产生总线拥塞从而出现丢包和数据错误的情况。</li>
<li><span class="tt">MX_CAN_FILTER_CNT</span>是最大的CAN接收过滤器数量，两个CAN共享标号0~27共28个过滤器。这部分内容比较繁杂，暂时不用理解，有兴趣自行参考MCU的数据手册。当前为简单起见，每个过滤器只设置一组规则用于控制一个id的过滤。</li>
<li><span class="tt">DEVICE_CAN_CNT</span>是MCU拥有的CAN硬件数量。</li>
<li><span class="tt">can_instance</span>是一个CAN实例。注意，CAN作为一个总线设备，一条总线上可以挂载多个设备，因此多个设备可以共享同一个CAN硬件。其成员变量包括发送id，发送邮箱（不需要管，只是一个32位变量，CAN收发器会自动设置其值），发送buff以及接收buff，还有接收id和接收协议解析回调函数。**由于目前使用的设备每个数据帧的长度都是8，因此收发buff长度暂时固定为8**。定义该结构体的时候使用了一个技巧，使得在结构体内部可以用结构体自身的指针作为成员，即<span class="tt">can_module_callback</span>的定义。</li>
<li><span class="tt">can_instance_config</span>是用于初始化CAN实例的结构，在调用CAN实例的初始化函数时传入（下面介绍函数时详细介绍）。</li>
<li><span class="tt">can_module_callback()</span>是模块提供给CAN接收中断回调函数使用的协议解析函数指针。对于每个需要CAN的模块，需要定义一个这样的函数用于解包数据。</li>
<li>每个使用CAN外设的module，都需要在其内部定义一个<span class="tt">can_instance*</span>。</li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md5"></a>
外部接口</h2>
<div class="fragment"><div class="line"><span class="keywordtype">void</span> CANRegister(can_instance* instance, can_instance_config config);</div>
<div class="line"><span class="keywordtype">void</span> CANSetDLC(CANInstance *_instance, uint8_t length); <span class="comment">// 设置发送帧的数据长度</span></div>
<div class="line">uint8_t CANTransmit(can_instance* _instance, uint8_t timeout);</div>
</div><!-- fragment --><p><span class="tt">CANRegister</span>是用于初始化CAN实例的接口，module层的模块对象（也应当为一个结构体）内要包含一个<span class="tt">usart_instance</span>。调用时传入实例指针，以及用于初始化的config。<span class="tt">CANRegister</span>应当在module的初始化函数内被调用，推荐config采用以下的方式定义，更加直观明了：</p>
<div class="fragment"><div class="line">can_instance_config config={.can_handle=&amp;hcan1,</div>
<div class="line">                            .tx_id=0x005,</div>
<div class="line">                            .rx_id=0x200,</div>
<div class="line">                            can_module_callback=MotorCallback}</div>
</div><!-- fragment --><p><span class="tt">CANTransmit()</span>是通过模块通过其拥有的CAN实例发送数据的接口，调用时传入对应的instance。在发送之前，应当给instance内的<span class="tt">send_buff</span>赋值。</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md6"></a>
私有函数和变量</h2>
<p>在.c文件内设为static的函数和变量</p>
<div class="fragment"><div class="line"><span class="keyword">static</span> can_instance *instance[MX_REGISTER_DEVICE_CNT]={NULL};</div>
</div><!-- fragment --><p>这是bsp层管理所有CAN实例的入口。</p>
<div class="fragment"><div class="line"><span class="keyword">static</span> <span class="keywordtype">void</span> CANServiceInit()</div>
<div class="line">static <span class="keywordtype">void</span> CANAddFilter(can_instance *_instance)</div>
<div class="line">static <span class="keywordtype">void</span> CANFIFOxCallback(CAN_HandleTypeDef *_hcan, uint32_t fifox)</div>
<div class="line"><span class="keywordtype">void</span> HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)</div>
<div class="line"><span class="keywordtype">void</span> HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)</div>
</div><!-- fragment --><ul>
<li><span class="tt">CANServiceInit()</span>会被<span class="tt">CANRegister()</span>调用，对CAN外设进行硬件初始化并开启接收中断和消息提醒。</li>
<li><span class="tt">CANAddFilter()</span>在每次使用<span class="tt">CANRegister()</span>的时候被调用，用于给当前注册的实例添加过滤器规则并设定处理对应<span class="tt">rx_id</span>的接收FIFO。过滤器的作用是减小CAN收发器的压力，只接收符合过滤器规则的报文（否则不会产生接收中断）。</li>
<li><span class="tt">HAL_CAN_RxFifo0MsgPendingCallback()</span>和<span class="tt">HAL_CAN_RxFifo1MsgPendingCallback()</span>都是对HAL的CAN回调函数的重定义（原本的callback是<span class="tt">__week</span>修饰的弱定义），当发生FIFO0或FIFO1有新消息到达的时候，对应的callback会被调用。<span class="tt">CANFIFOxCallback()</span>随后被前两者调用，并根据接收id和硬件中断来源（哪一个CAN硬件，CAN1还是CAN2）调用对应的instance的回调函数进行协议解析。</li>
<li>当有一个模块注册了多个can实例时，通过<span class="tt">CANInstance.id</span>,使用强制类型转换将其转换成对应模块的实例指针，就可以对不同的模块实例进行回调处理了。</li>
</ul>
<h2 class="doxsection"><a class="anchor" id="autotoc_md7"></a>
注意事项</h2>
<p>由于CAN总线自带发送检测，如果总线上没有挂载目标设备（接收id和发送报文相同的设备），那么CAN邮箱会被占满而无法发送。在<span class="tt">CANTransmit()</span>中会对CAN邮箱是否已满进行<span class="tt">while(1)</span>检查。当超出<span class="tt">timeout</span>之后函数会返回零，说明发送失败。</p>
<p>由于卡在<span class="tt">while(1)</span>处不断检查邮箱是否空闲，调用<span class="tt">CANTransmit()</span>的任务可能无法按时挂起，导致任务定时不精确。建议在没有连接CAN进行调试时，按需注释掉有关CAN发送的代码部分，或设定一个较小的<span class="tt">timeout</span>值，防止对其他需要精确定时的任务产生影响。 </p>
</div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<div id="page-nav" class="page-nav-panel">
<div id="page-nav-resize-handle"></div>
<div id="page-nav-tree">
<div id="page-nav-contents">
</div><!-- page-nav-contents -->
</div><!-- page-nav-tree -->
</div><!-- page-nav -->
</div><!-- container -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
  <ul>
    <li class="footer">Generated by <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.14.0 </li>
  </ul>
</div>
</body>
</html>
